﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UtilZ.Dotnet.Ex.Base;
using UtilZ.Dotnet.Ex.Model;
using UtilZ.Dotnet.Ex.ZLog;

namespace UtilZ.Dotnet.Ex.Communication.Net.Ftp
{
    /// <summary>
    /// 如果指定了文件或目录，返回其信息；否则返回当前工作目录的信息
    /// Returns information of a file or directory if specified, else information of the current working directory is returned.
    /// </summary>
    internal class FtpLISTCommand : FtpCommandAbs
    {
        /***********************************************************************************************************
        * [LIST]（List，列表）命令
        * 说明：让服务器给客户发送一份列表。
        * 
        * 用法：LIST[<PathName>]<CRLF>
        * 参数：PathName是服务器系统上的一个有效路径和文件规范。
        * 例子：
        * SendData "LIST pub/*.*" + “/r/n
        * 
        * 注释：如果路径名指定的是一个目录或其他文件组，服务器传送一份位于指定目录中的文件的列表。如果路径名指定的是一个文件，那么服务应该发送此文件 的当前信息。空变元则暗指用户的当前工作目录或默认目标。数据将在类型ASCII或类型EBCDIC中通过数据连接传送（用户必须确保ASII或 EBCDIC的类型适当）。
        * 因为系统与系统之间的文件信息可能有很大差别，所以这项信息要在程序中自动使用可能很困难，但对用户来说会很有用。
        * 返回值如下（粗体表示成功）：
        * 125  数据连接已打开，传输启动。
        * 150  文件状态没问题，准备打开数据连接。
        * 226  关闭数据连接，请求的文件操作已成功。
        * 250  请求的文件操作正常进行，已完成。
        * 421  服务不可用，关闭控制连接。
        * 
        * 如果某项服务获知自己即将关闭，会向所有命令做出这个应答。
        * 425  无法打开数据连接。
        * 426  连接关闭，传输中止。
        * 450  请求的文件操作无法执行，文件不可用（例如文件正忙）。
        * 451  请求的操作被中止，处理中发生本地错误。
        * 500  语法错误，无法识别命令。这其中包括命令行过长之类的错误。
        * 501  参数或变元中有语法错误。
        * 502  命令还没有被实现。
        * 530  无法登录。
        * 
        ************************************************************************************************************/


        public FtpLISTCommand()
            : base()
        {

        }

        protected override string[] GetNames()
        {
            return new string[] { FtpCommands.LIST };
        }

        protected override bool CheckLastCommand(FtpSession session)
        {
            if (session.LastCommandIsTranferCommand())
            {
                return true;
            }

            return false;
        }

        protected override IFtpResponse PrimitiveExcute(FtpSession session, string command, string commandLine)
        {
            //Request: LIST
            //Response: 125 Data connection already open; Transfer starting.
            //...传输数据,传输完成后,再发送下一个响应
            //Response: 226 Transfer complete.
            //Response: 550 Access is denied. 

            try
            {
                if (!session.AllowRead())
                {
                    return new FtpCodeResponse(FtpStatusCodes.Code550, "Access is denied. ");
                }

                //ZLoger.Warn($"begin1 {nameof(FtpLISTCommand.PrimitiveExcute)}");
                using (var tempDataTranfer = session.GetDataTranferObject())
                {
                    //ZLoger.Warn($"begin2 {nameof(FtpLISTCommand.PrimitiveExcute)}");
                    session.AddDataTranfer(tempDataTranfer);

                    //ZLoger.Warn($"begin3 {nameof(FtpLISTCommand.PrimitiveExcute)}");
                    IFtpResponse response;
                    var result = tempDataTranfer.CreateSendDataTcpClient(out response);
                    if (result)
                    {
                        //ZLoger.Warn($"begin4 {nameof(FtpLISTCommand.PrimitiveExcute)}");
                        session.Send(response);

                        //ZLoger.Warn($"begin5 {nameof(FtpLISTCommand.PrimitiveExcute)}");
                        DataSource bufferData = this.GetList(session);
                        tempDataTranfer.SendData(bufferData);

                        response = new FtpCodeResponse(FtpStatusCodes.Code226, "Transfer complete");
                    }

                    //ZLoger.Warn($"begin6 {nameof(FtpLISTCommand.PrimitiveExcute)}");
                    return response;
                }

                //ZLoger.Warn($"begin7 {nameof(FtpLISTCommand.PrimitiveExcute)}");
            }
            catch (Exception ex)
            {
                return new FtpCodeResponse(FtpStatusCodes.Code451, ex.Message);
            }
        }

        private DataSource GetList(FtpSession session)
        {
            string str = session.LIST();
            byte[] buffer = session.Encoding.GetBytes(str);
            return new DataSource(buffer);
        }
    }

}
